package com.magina.machine.controller;


import com.magina.machine.config.builder.FormStateMachineBuilder;
import com.magina.machine.config.builder.OrderStateMachineBuilder;
import com.magina.machine.model.DemoClass;
import com.magina.machine.model.FormMachineData;
import com.magina.machine.model.FormMachineData.*;
import com.magina.machine.model.OrderMachinaData.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.persist.StateMachinePersister;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.Date;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;

@RestController
@RequestMapping("/status")
public class StatusController {

    private Logger log = LoggerFactory.getLogger(getClass());

    @Autowired
    private StateMachine orderSingleMachine;

    @Autowired
    private OrderStateMachineBuilder orderStateMachineBuilder;

    @Autowired
    private FormStateMachineBuilder formStateMachineBuilder;

    @Autowired
    private BeanFactory beanFactory;

    @Autowired
    @Qualifier("orderMemoryPersister")
    private StateMachinePersister<OrderStates, OrderEvents, String> orderMemoryPersister;

    @Autowired
    @Qualifier("orderRedisPersister")
    private StateMachinePersister<OrderStates, OrderEvents, String> orderRedisPersister;

    @Resource(name="beanPersister")
    private StateMachinePersister<OrderStates, OrderEvents, DemoClass> beanPersister;



    //此种情况 是初级的状态机,无标识id.只能启动状态变更和出发事件.缺点.无法传参.无法多状态机.单例多线程无法共享
    @GetMapping("/testSingleOrderState")
    public void testSingleOrderState() {
        // 创建流程
        orderSingleMachine.start();
        // 触发PAY事件
        orderSingleMachine.sendEvent(OrderEvents.PAY);
        // 触发RECEIVE事件
        orderSingleMachine.sendEvent(OrderEvents.RECEIVE);
        // 获取最终状态
        log.info("当前线程: {} ,最终状态: {}",Thread.currentThread().getName(),orderSingleMachine.getState().getId());
        orderSingleMachine.stop();
    }

    @GetMapping("/testSingleOrderStateUnderConcurrent")
    public void testSingleOrderStateUnderConcurrent() throws Exception {
        AtomicInteger count = new AtomicInteger(5);
        while (count.get() > 0){
            log.info("current cout :{}",count.get());
            new Thread(() -> {
                try {
                    //多线程下 证明orderSingleMachine是单例模式.只有一个thread能执行machine
//                    this.testSingleOrderState();

                    //用工厂可以多线程单例
//                    this.testOrderStateWithConcurrent();

                    //实现不同id的同种machine
                    this.testFormStateWithConcurrent();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }).start();
            count.decrementAndGet();
        }
    }

    // question: 解决多线程下状态机复用
    @GetMapping("/testOrderStateWithConcurrent")
    public void testOrderStateWithConcurrent() throws Exception {
        StateMachine<OrderStates, OrderEvents> stateMachine = orderStateMachineBuilder.build(beanFactory);
        System.out.println(stateMachine.getId());
        stateMachine.start();
        stateMachine.sendEvent(OrderEvents.PAY);
        //params
        DemoClass demoClass = DemoClass.builder().name(String.format("testId:%s",UUID.randomUUID())).createDate(new Date()).build();
        Message<OrderEvents> message = MessageBuilder.withPayload(OrderEvents.RECEIVE).setHeader("demoClass", demoClass).setHeader("otherParam", "otherParamValue").build();
        stateMachine.sendEvent(message);
        log.info("当前线程: {} ,最终状态: {}",Thread.currentThread().getName(),stateMachine.getState().getId());
        stateMachine.stop();
    }


    // question: 解决不同类型的状态机
    @GetMapping("/testFormStateWithConcurrent")
    public void testFormStateWithConcurrent() throws Exception {
        //可以使用业务数据的id作为machineId,涉及到后面的persister
        StateMachine<FormStates, FormEvents> stateMachine = formStateMachineBuilder.build(FormMachineData.MACHINE_ID_ONE,beanFactory);
        System.out.println(stateMachine.getId());
        stateMachine.start();
        stateMachine.sendEvent(FormEvents.WRITE);
        stateMachine.sendEvent(FormEvents.CONFIRM);
        //params
        DemoClass demoClass = DemoClass.builder().name(String.format("testId:%s",UUID.randomUUID())).createDate(new Date()).build();
        Message<FormEvents> message = MessageBuilder.withPayload(FormEvents.SUBMIT).setHeader("demoClass", demoClass).setHeader("otherParam", "otherParamValue").build();
        stateMachine.sendEvent(message);
        log.info("当前线程: {} ,最终状态: {}",Thread.currentThread().getName(),stateMachine.getState().getId());
        stateMachine.stop();
    }

    // question: 解决用状态机解耦后的传参问题.
    @GetMapping("/testOrderStateWithParams")
    public void testOrderStateWithParams() throws Exception {
        StateMachine<OrderStates, OrderEvents> stateMachine = orderStateMachineBuilder.build(beanFactory);
        System.out.println(stateMachine.getId());
        stateMachine.start();
        stateMachine.sendEvent(OrderEvents.PAY);
        //params
        DemoClass demoClass = DemoClass.builder().name(String.format("testId:%s",UUID.randomUUID())).createDate(new Date()).build();
        Message<OrderEvents> message = MessageBuilder.withPayload(OrderEvents.RECEIVE).setHeader("demoClass", demoClass).setHeader("otherParam", "otherParamValue").build();
        stateMachine.sendEvent(message);
        log.info("当前线程: {} ,最终状态: {}",Thread.currentThread().getName(),stateMachine.getState().getId());
        stateMachine.stop();
    }

    // 内存持久化或者用redis持久化
    @RequestMapping("/testMemoryPersister")
    public void tesMemorytPersister(String id) throws Exception {
        StateMachine<OrderStates, OrderEvents> stateMachine = orderStateMachineBuilder.build(beanFactory);
        stateMachine.start();
        stateMachine.sendEvent(OrderEvents.PAY);
        log.info("当前线程: {} ,当前状态: {}",Thread.currentThread().getName(),stateMachine.getState().getId());
        //持久化stateMachine
//        orderMemoryPersister.persist(stateMachine, id);
        orderRedisPersister.persist(stateMachine, id);
        DemoClass demoClass = DemoClass.builder().name(id).createDate(new Date()).build();
        Message<OrderEvents> message = MessageBuilder.withPayload(OrderEvents.RECEIVE).setHeader("demoClass", demoClass).setHeader("otherParam", "otherParamValue").build();
        stateMachine.sendEvent(message);
        log.info("当前线程: {} ,当前状态: {}",Thread.currentThread().getName(),stateMachine.getState().getId());
        Thread.sleep(3000L);
        //此处模拟的是 pay事件完成 后persister machine .再 发送receive事件,取出machine还是持久化那个阶段的状态
//        orderMemoryPersister.restore(stateMachine, id);
        orderRedisPersister.restore(stateMachine, id);
        log.info("当前线程: {} ,当前状态: {}",Thread.currentThread().getName(),stateMachine.getState().getId());
    }

    //用自定义持久化,触发事件
    @RequestMapping("/testBeanRestore")
    public void testOrderRestore(String id) throws Exception {
        StateMachine<OrderStates, OrderEvents> stateMachine = orderStateMachineBuilder.build(beanFactory);
        //订单
        DemoClass demoClass = DemoClass.builder()
                .uuid(UUID.randomUUID().toString())
                .createDate(new Date())
                .name("testName")
                .status(OrderStates.WAITING_FOR_RECEIVE.toString())
                .build();
        //恢复
        beanPersister.restore(stateMachine, demoClass);
        Message<OrderEvents> message = MessageBuilder.withPayload(OrderEvents.RECEIVE)
                .setHeader("demoClass", demoClass)
                .setHeader("otherParam", "otherParamValue").build();
        stateMachine.sendEvent(message);
        //查看恢复后状态机的状态
        log.info("当前线程: {} ,当前状态: {}",Thread.currentThread().getName(),stateMachine.getState().getId());
    }



}
